Skip to content

fix(jid): make agent identity, the AD form, and device dedup agree - #1182

Merged
jlucaso1 merged 6 commits into
mainfrom
fix/jid-agent-identity-and-ad-form
Jul 29, 2026
Merged

fix(jid): make agent identity, the AD form, and device dedup agree#1182
jlucaso1 merged 6 commits into
mainfrom
fix/jid-agent-identity-and-ad-form

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Replaces #1180 and #1181, which were stacked. Same work, one diff against main, so a reviewer sees the whole thing at once.

Follow-up to #1178. That PR stopped the decoder writing the AD-JID domain byte into agent; this makes the rest of the codebase agree on what a JID's identity actually is.

1. An inert agent is not identity

agent is only meaningful where the server renders it. On Pn/Lid/Hosted/HostedLid the wire spells the server as a domain byte instead — nothing encodes an agent set there (server_to_domain_type), nothing prints it (renders_agent), nothing hashes it. Two JIDs differing only there address the same device.

It was in the derived PartialEq/Hash anyway, so a JID took on a different identity depending on where it was parsed from — the wire, or the store, which keeps JIDs as text.

PartialEq/Hash are now written out for Jid and JidRef plus the cross-type comparison, all through one identity_agent so they cannot drift. This is the rule is_same_chat_as already used; it just was not the rule == used.

2. The hashed form writes .0, matching WA Web

The formatter behind the participant hash (push_ad_to, renamed in §4) wrote self.agent into the agent position. WA Web writes a literal ".0" there and never reads an agent off its Wid:

// docs/captured-js/WAWeb/Phash/Utils.js — phashV2
e.map(e => e.toString({ legacy: !0, formatFull: !0 })).sort().join("")

// docs/captured-js/WAWeb/Wid.js:127
(t.formatFull === !0 || t.formatIncludeAgent === !0) && (e = ".0");

WAWebWid has no agent field at all, and the .0 is unconditional — there is no per-server carve-out. This string is what the server recomputes to validate the phash, so any JID carrying an agent produced a hash the server would reject — a pre-existing parity bug, reachable because swap_pn_lid_namespace preserves agent across namespace conversion.

The only production caller is participant_list_hash. This also makes the phash memo sound: it keys by JID, so equal JIDs must produce equal strings.

3. Device dedup keyed on the raw agent

sort_dedup_by_device sorted and deduplicated on (user, server, agent, device), which is wrong in both directions:

  • keying on the raw agent lets two JIDs that are one device (an inert agent on an AD server — same AD-JID, same Signal address) both survive, giving one session two concurrent encryption jobs;
  • dropping the agent entirely collapses two genuinely different @bot/@interop devices, which do render it, silently losing a fan-out destination.

It now keys on exactly what == compares: user, server, device, integrator, and identity_agent. Jid::identity_agent is public so callers building their own key over a JID can use the same rule instead of the raw field.

4. The formatter is named for what it is

push_ad_to / to_ad_string read as a general AD rendering, but the agent position is a literal 0 and the only caller is the participant hash — a name that invites exactly the misuse §2 fixed. Renamed to push_phash_form_to / to_phash_form_string, with the doc pointing at Display for the addressed form.

What is deliberately NOT included

integrator stays in identity. An earlier revision normalised it off interop, on the grounds that the bundle only ever produces it there (createInteropJid, and the JID_INTEROP branch of toString — it exists on no other JID type). That part is true, but is_same_chat_as and jids_share_user_identity compare it unconditionally, and making == disagree with them reached real decisions: quote remote_jid construction, and a retry path that refuses a direct retransmission. Not worth the contradiction for a value the wire never produces there.

Breaking changes

Taken deliberately — the crate is pre-1.0.

  • Jid::normalize_for_prekey_bundle is removed, with its call sites and the encryption_jid.agent = 0 in encrypt.rs. Both existed to clear a field that no longer reaches identity.
  • Jid::push_ad_to / to_ad_string are renamed to push_phash_form_to / to_phash_form_string.
  • Three public payloads now carry the server's raw agent instead of a scrubbed one, because nothing scrubs it any more: PreKeyFetchOutcome::bundles keys, RejectedDevice::jid, and — via the encryption JID returned on IdentityChange::ReplacedExisting — the serializable IdentityChange.user event. The value is inert for identity, so lookups and comparisons are unaffected; only the rendered/serialised field differs.

Verification

Four tests, covering the changes above:

  • inert_agent_stays_out_of_identity — for each AD server: agent not splitting identity, Hash agreeing with Eq on every such pair, owned/borrowed/cross-type agreeing, integrator still splitting and agreeing with is_same_chat_as; and the other direction, where interop does render the agent and it still splits.
  • phash_form_writes_the_agent_position_as_zero_like_wa_web — pins the spelling, and the equal-JIDs-equal-strings property the phash memo depends on.
  • device_dedup_collapses_jids_that_differ_only_in_an_inert_agent — asserts one identity and one Signal address as preconditions, then that the dedup collapses them, and that a different device still survives.
  • device_dedup_keeps_agents_apart_where_the_server_renders_them — the mirror case: @bot/@interop agents, and interop integrators, must NOT be merged.

Two existing tests changed. Both asserted that a prekey bundle stays invisible until the lookup key is normalised — the workaround this removes — and their doc comments name the symptom it caused: "No pre-key bundle returned". They now assert the bundle is found either way. They were pinning the defect, not guarding against one.

Suites: 125 wacore-binary, 1471 wacore (--features voip), 1281 whatsapp-rust. Clippy clean. cargo miri test -p wacore-binary --lib green (115).

Provenance

Sections 2, 3 and 4 exist because reviews pushed back on the identity change. Verifying the objections against docs/captured-js showed one was pointing at a real bug — ours, not the PR's — while another rested on a premise the bundle contradicts (there is no per-server carve-out around WA Web's .0; it is unconditional). The integrator reversal and the both-directions dedup fix came from the same passes.

jlucaso1 added 3 commits July 29, 2026 00:35
`agent` is only meaningful where the server renders it. On
Pn/Lid/Hosted/HostedLid the wire spells the server as a domain byte
instead, and `Display` omits any agent set there. `integrator` only
exists on interop. Off those servers neither field is encoded, printed,
or read back — two JIDs differing only there address the same thing.

They were in the derived `PartialEq`/`Hash` anyway, so a JID picked up a
different identity depending on where it was parsed from. #1178 stopped
the decoder minting one such difference; this stops the difference from
mattering at all, wherever it comes from.

`PartialEq` and `Hash` are now written out for both `Jid` and `JidRef`,
plus the cross-type comparison, all routed through one `identity_extras`
so they cannot disagree — a `Hash` that disagreed with `Eq` would put an
entry in a `HashMap<Jid, _>` that could never be looked up again.

The two tests that broke were the ones asserting the old behaviour: both
existed to prove a bundle stays invisible until the lookup key is
normalised, which is the workaround this removes. They now assert the
bundle is found either way.
`push_ad_to` wrote `self.agent` into the agent position. WA Web writes a
literal `.0` there: its `formatFull` spelling hardcodes the string, and
its Wid has no agent field to read one from (`WAWebWid`). The captured
bundle is the ground truth here — this is the string the server
recomputes to validate the phash, so a JID that carried an agent
produced a hash the server would reject.

The only production caller is `participant_list_hash`; the rest are
tests, the fuzz target and a bench.

This also closes the hole a reviewer flagged in the identity change:
`ResolvedGroupDevices::phash` memoises by JID, so two JIDs that compare
equal must produce the same AD string, or the memo can serve a hash
computed for a different one. They now do.
Two corrections from an adversarial review of the identity change.

`integrator` is no longer normalised. It is only ever non-zero on interop,
but `is_same_chat_as` and `jids_share_user_identity` compare it
unconditionally — folding it into `==` made those disagree, and the
disagreement reached real decisions (quote `remote_jid`, and a retry that
refuses a direct retransmission). Equality now matches them.

`sort_dedup_by_device` keyed on the raw `agent`, which contradicts the
rule the rest of the change establishes: two AD JIDs differing only there
are one device, encode to the same AD-JID, and resolve to the same Signal
address — yet both survived the dedup the group fan-out relies on to
collapse duplicate destinations, so one session could get two concurrent
encryption jobs. Latent before, but formalising the identity made it a
contradiction. Now keyed on user/server/device.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a42fb75c-de4c-459f-a649-16232c8d6acb

📥 Commits

Reviewing files that changed from the base of the PR and between d6b22f6 and 0f202f3.

📒 Files selected for processing (1)
  • wacore/binary/fuzz/fuzz_targets/parse_jid.rs

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved JID identity handling so inert agent values no longer affect matching, equality, hashing, or device deduplication.
    • Fixed pre-key and session lookups for LID devices by preserving server-provided JID data and using consistent JID keys.
    • Updated participant-list hashing to use the correct standardized phash form representation.
  • Tests

    • Added and updated coverage for JID identity semantics, device deduplication, pre-key lookup, session establishment, and participant hashing behavior.

Walkthrough

Changes

JID equality and hashing now normalize inert agent values for non-rendering servers. Phash strings encode .0, device deduplication follows effective identity, and prekey/session flows use raw JID keys with equality-based matching.

JID identity and prekey flow

Layer / File(s) Summary
Normalize JID identity and phash rendering
wacore/binary/src/jid.rs, wacore/binary/benches/jid_benchmark.rs, wacore/binary/fuzz/fuzz_targets/parse_jid.rs
Manual equality and hashing normalize inert agents, phash formatting emits .0, and related tests, fuzz checks, and benchmarks use the new APIs.
Deduplicate device JIDs by effective identity
wacore/src/types/jid.rs
sort_dedup_by_device includes identity_agent and integrator, with coverage for inert-agent collapse and agent-significant servers.
Build participant hashes from phash form
wacore/src/messages.rs
Participant list hashing and reference tests now use phash-form strings.
Use direct JID keys for prekey sessions
wacore/src/prekeys.rs, src/client/sessions.rs, wacore/src/send/encrypt.rs, wacore/src/send/tests.rs
Prekey parsing, identity collection, session establishment, and encryption setup preserve raw JIDs and validate direct lookups.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PrekeyResponse
  participant SessionSetup
  participant PrekeyMap
  PrekeyResponse->>PrekeyMap: store parsed raw JID
  SessionSetup->>PrekeyMap: look up requested JID directly
  PrekeyMap-->>SessionSetup: match by normalized JID identity
Loading

Possibly related PRs

Suggested labels: breaking-change

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes to JID identity, phash formatting, and device deduplication.
Description check ✅ Passed The description is clearly about the same JID identity and formatting changes described by the diff.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/jid-agent-identity-and-ad-form

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59a1f01cf8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wacore/src/types/jid.rs Outdated
jids.dedup_by(|a, b| {
a.user == b.user && a.server == b.server && a.agent == b.agent && a.device == b.device
});
jids.dedup_by(|a, b| a.user == b.user && a.server == b.server && a.device == b.device);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve rendered agents during device deduplication

When a device list contains @bot or @interop JIDs with the same user/device but different agents, this predicate collapses them even though those servers render the agent and the new Jid::eq implementation correctly treats them as distinct identities. Any fan-out using this helper can therefore silently drop one valid destination; normalize the agent only for servers where renders_agent() is false, while retaining it in both the sort key and dedup predicate elsewhere.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wacore/binary/src/jid.rs (1)

766-787: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the public AD formatter restricted.

push_ad_to is fine as “literal .0 for the phash”, but because to_ad_string() publically delegates to it, callers using to_ad_string() for Bot/Interop JIDs will also get the wrong agentless form. Either keep push_ad_to private to the phash path or rename/adjust it so no production caller depends on it elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacore/binary/src/jid.rs` around lines 766 - 787, The public AD formatting
path must not reuse the phash-specific literal “.0” formatter. Restrict
push_ad_to to the participant_list_hash path or rename it to clearly mark that
scope, and update that caller accordingly; ensure to_ad_string() continues using
the agent-aware formatter required for Bot/Interop JIDs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wacore/src/types/jid.rs`:
- Around line 111-126: Clarify the documentation for sort_dedup_by_device to
state that its user/server/device key intentionally omits Jid::eq’s integrator
field and assumes integrator is zero for fan-out inputs. Keep the existing
deduplication behavior unchanged, and explicitly describe the interop/integrator
carve-out so the identity claim is precise.

---

Outside diff comments:
In `@wacore/binary/src/jid.rs`:
- Around line 766-787: The public AD formatting path must not reuse the
phash-specific literal “.0” formatter. Restrict push_ad_to to the
participant_list_hash path or rename it to clearly mark that scope, and update
that caller accordingly; ensure to_ad_string() continues using the agent-aware
formatter required for Bot/Interop JIDs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5f429bf0-3f35-460e-a5f8-012cb6edafbe

📥 Commits

Reviewing files that changed from the base of the PR and between b4f5041 and 59a1f01.

📒 Files selected for processing (4)
  • src/client/sessions.rs
  • wacore/binary/src/jid.rs
  • wacore/src/send/tests.rs
  • wacore/src/types/jid.rs

Comment thread wacore/src/types/jid.rs Outdated
Comment on lines 111 to 126
/// Sort and deduplicate by device identity (user + server + device).
///
/// Keyed on the same rule as `Jid`'s equality, and deliberately not on the raw
/// `agent`: on the AD servers an agent is not part of the device's identity, so
/// two JIDs carrying different ones encode to the same AD-JID and resolve to the
/// same Signal address. Keying on it here would let both survive and give the
/// group fan-out two jobs against one session.
pub fn sort_dedup_by_device(jids: &mut Vec<Jid>) {
jids.sort_unstable_by(|a, b| {
a.user
.cmp(&b.user)
.then_with(|| a.server.cmp(&b.server))
.then_with(|| a.agent.cmp(&b.agent))
.then_with(|| a.device.cmp(&b.device))
});
jids.dedup_by(|a, b| {
a.user == b.user && a.server == b.server && a.agent == b.agent && a.device == b.device
});
jids.dedup_by(|a, b| a.user == b.user && a.server == b.server && a.device == b.device);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tighten the doc claim — this key isn't actually "the same rule as Jid's equality."

Jid::eq folds in integrator; this dedup key doesn't and never has. Right now that's probably fine — integrator is supposed to be nonzero only for interop, which shouldn't be in this fan-out list — but if that assumption ever breaks, this function will silently collapse two genuinely different devices into one, and someone won't find out until a message goes to the wrong place. Either tighten the doc to spell out the integrator carve-out explicitly, or add a debug_assert to catch a caller violating it. I want us to be precise about what "identity" means here, not just directionally correct.

📝 Doc clarification
-/// Sort and deduplicate by device identity (user + server + device).
-///
-/// Keyed on the same rule as `Jid`'s equality, and deliberately not on the raw
-/// `agent`: on the AD servers an agent is not part of the device's identity, so
+/// Sort and deduplicate by device identity (user + server + device).
+///
+/// Deliberately narrower than `Jid`'s equality: it also omits `integrator`,
+/// relying on callers never passing interop-addressed JIDs (where `integrator`
+/// is meaningful) into this fan-out path. It is not keyed on the raw `agent`:
+/// on the AD servers an agent is not part of the device's identity, so
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Sort and deduplicate by device identity (user + server + device).
///
/// Keyed on the same rule as `Jid`'s equality, and deliberately not on the raw
/// `agent`: on the AD servers an agent is not part of the device's identity, so
/// two JIDs carrying different ones encode to the same AD-JID and resolve to the
/// same Signal address. Keying on it here would let both survive and give the
/// group fan-out two jobs against one session.
pub fn sort_dedup_by_device(jids: &mut Vec<Jid>) {
jids.sort_unstable_by(|a, b| {
a.user
.cmp(&b.user)
.then_with(|| a.server.cmp(&b.server))
.then_with(|| a.agent.cmp(&b.agent))
.then_with(|| a.device.cmp(&b.device))
});
jids.dedup_by(|a, b| {
a.user == b.user && a.server == b.server && a.agent == b.agent && a.device == b.device
});
jids.dedup_by(|a, b| a.user == b.user && a.server == b.server && a.device == b.device);
}
/// Sort and deduplicate by device identity (user + server + device).
///
/// Deliberately narrower than `Jid`'s equality: it also omits `integrator`,
/// relying on callers never passing interop-addressed JIDs (where `integrator`
/// is meaningful) into this fan-out path. It is not keyed on the raw `agent`:
/// on the AD servers an agent is not part of the device's identity, so
/// two JIDs carrying different ones encode to the same AD-JID and resolve to the
/// same Signal address. Keying on it here would let both survive and give the
/// group fan-out two jobs against one session.
pub fn sort_dedup_by_device(jids: &mut Vec<Jid>) {
jids.sort_unstable_by(|a, b| {
a.user
.cmp(&b.user)
.then_with(|| a.server.cmp(&b.server))
.then_with(|| a.device.cmp(&b.device))
});
jids.dedup_by(|a, b| a.user == b.user && a.server == b.server && a.device == b.device);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacore/src/types/jid.rs` around lines 111 - 126, Clarify the documentation
for sort_dedup_by_device to state that its user/server/device key intentionally
omits Jid::eq’s integrator field and assumes integrator is zero for fan-out
inputs. Keep the existing deduplication behavior unchanged, and explicitly
describe the interop/integrator carve-out so the identity claim is precise.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 10.06 MiB 10.06 MiB +4.88 KiB (+0.05%) 🔺
bin .text 8.09 MiB 8.10 MiB +5.38 KiB (+0.06%) 🔺
bin allocated (text+data+bss) 10.05 MiB 10.06 MiB +4.04 KiB (+0.04%) 🔺
llvm-lines wacore 490,022 490,082 +60 (+0.01%) 🔺
llvm-lines wacore copies 16,314 16,325 +11 (+0.07%) 🔺
llvm-lines whatsapp-rust lib 722,899 722,727 -172 (-0.02%) 🔽
llvm-lines whatsapp-rust lib copies 22,787 22,788 +1 (+0.00%) 🔺
deps crates (Cargo.lock) 471 471 0
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.84 MiB 1.84 MiB -2.83 KiB (-0.15%) 🔽
.text wacore 662.83 KiB 668.38 KiB +5.55 KiB (+0.84%) 🔺
.text wacore_binary 89.30 KiB 89.16 KiB -139 B (-0.15%) 🔽
.text wacore_libsignal 166.27 KiB 166.27 KiB 0
.text wacore_appstate 22.35 KiB 22.35 KiB 0
.text wacore_noise 21.79 KiB 21.79 KiB 0
.text waproto 1.74 MiB 1.74 MiB 0
.text whatsapp_rust_sqlite_storage 515.98 KiB 515.98 KiB 0
.text whatsapp_rust_tokio_transport 40.36 KiB 40.36 KiB 0
.text whatsapp_rust_ureq_http_client 10.33 KiB 10.33 KiB 0
.text std 1.07 MiB 1.07 MiB -45 B (-0.00%) 🔽
.text other deps 1.90 MiB 1.91 MiB +2.91 KiB (+0.15%) 🔺
Top movers (cargo-bloat attribution)
Crate main PR Δ
wacore 662.83 KiB 668.38 KiB +5.55 KiB (+0.84%)
whatsapp_rust 1.84 MiB 1.84 MiB -2.83 KiB (-0.15%)
hashbrown 2.38 KiB 5.05 KiB +2.67 KiB (+111.88%)

Baseline: 0c6950bd3 (latest main run) · Head: cb0a5863d · Graphs

jlucaso1 added 2 commits July 29, 2026 01:01
Three corrections from review.

`sort_dedup_by_device` dropped `agent` entirely, which was wrong in the
other direction: `@bot`/`@interop` DO render the agent, `==` treats those
JIDs as different devices, and collapsing them silently loses a fan-out
destination. It now keys on exactly what equality compares — user,
server, device, integrator, `identity_agent` — so the two cannot
disagree either way. `Jid::identity_agent` is public for callers that
build their own key over a JID.

`push_ad_to`/`to_ad_string` are renamed to `push_phash_form_to`/
`to_phash_form_string`. The old names read as a general AD rendering,
but the agent position is a literal `0` (WA Web's `formatFull`, which
hardcodes it with no per-server carve-out) and the only caller is the
participant hash. The doc now points at `Display` for the addressed form.

`normalize_for_prekey_bundle` is gone, with its call sites and the
`encryption_jid.agent = 0` in encrypt.rs. Both existed to clear a field
that no longer reaches identity. This is breaking twice over — a public
method disappears, and the raw agent now survives on the public
`PreKeyFetchOutcome::bundles` keys, `RejectedDevice::jid`, and the
`IdentityChange.user` event payload — which is acceptable pre-1.0.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6b22f60c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wacore/binary/src/jid.rs
pub fn to_ad_string(&self) -> String {
/// See [`Jid::push_phash_form_to`]. Not a general JID rendering — use
/// `Display`/`to_string` for that.
pub fn to_phash_form_string(&self) -> String {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the detached fuzz target after renaming the formatter

Running cargo fuzz run parse_jid now fails to compile because wacore/binary/fuzz/fuzz_targets/parse_jid.rs:88 still calls the removed Jid::to_ad_string() method. This fuzz crate is intentionally detached from the main workspace, so the normal workspace build and clippy jobs do not catch the stale call; update the target and its nearby comment to use to_phash_form_string().

Useful? React with 👍 / 👎.

`parse_jid.rs` still called `to_ad_string`, so `cargo fuzz run parse_jid`
no longer compiled. The fuzz crate is deliberately outside the workspace,
so neither the build nor the clippy job covers it — the rename passed CI
with the target broken.
@jlucaso1
jlucaso1 merged commit c097b8f into main Jul 29, 2026
19 of 21 checks passed
@jlucaso1
jlucaso1 deleted the fix/jid-agent-identity-and-ad-form branch July 29, 2026 05:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f202f319f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wacore/src/types/jid.rs
&j.user,
j.server,
j.device,
j.integrator,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude integrator from wire-device deduplication

When a resolved set contains two JIDs that differ only in integrator, this key now retains both even though wacore/src/send/group.rs:249-254 defines this helper's group-send identity as excluding integrator because it is absent from the <to jid> and phash forms. Both Jid::push_phash_form_to and write_signal_address_to also omit the field, so the surviving entries become duplicate hash inputs and encryption jobs for the same wire/Signal destination; keep integrator out of this wire-device key rather than forcing it to match the broader Jid equality relation.

Useful? React with 👍 / 👎.

jlucaso1 added a commit that referenced this pull request Jul 29, 2026
…atch

`PartialEq` normalised both sides through `identity_agent` on every
comparison. Equal raw agents are already equal identity agents — the
servers matched one line above, so both sides normalise the same way —
and that is the overwhelmingly common case, since nothing off the wire
carries an agent on the AD servers.

Measured A/B in a single binary, pinned core, with an unmodified control,
three rounds: `Jid::eq` 2.39 ns -> 2.12 ns, **-11%**. That recovers most
of what #1182 cost equality.

The shortcut is load-bearing on the `self.server == other.server` check
preceding it; the comment says so, because reordering the conjunction
would silently make it wrong.

Not applied to `Hash`: the same shortcut measured +4.6% there, since the
SipHash dominates and the extra branch does not pay for itself.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant